﻿using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace Crossroad.Utils.Extensions
{
    /// <summary>
    /// 基础对象扩展
    /// 编码：陈冉
    /// 2018-2-27 14:00:36
    /// </summary>
    public static class ObjectExt
    {

        /// <summary>
        /// 对象是否等于NULL。
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsNull(this object value)
        {
            return value == null;
        }

        /// <summary>
        /// 对象是否不等于NULL。
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool NotNull(this object value)
        {
            return value != null;
        }

        /// <summary>
        /// 对象转json字符串
        /// </summary>
        /// <param name="input"></param>
        /// <param name="isSnake"></param>
        /// <returns></returns>
        public static string ToJson(this object input, bool isSnake = false)
        {
            if (!isSnake) return JsonConvert.SerializeObject(input);

            var settings = new JsonSerializerSettings
            {
                ContractResolver = new DefaultContractResolver
                {
                    NamingStrategy = new SnakeCaseNamingStrategy { ProcessDictionaryKeys = true }
                },
                Formatting = Formatting.Indented
            };

            return JsonConvert.SerializeObject(input, settings);
        }

        /// <summary>
        /// 获取枚举名称
        /// </summary>
        /// <param name="input"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string EnumName(this object input)
        {
            try
            {
                return int.TryParse(input.ToString(), out var val) ? Enum.GetName(input.GetType(), val) : "";
            }
            catch
            {
                return "";
            }
        }

        /// <summary>
        /// 转换参数字符串
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string ToParamsStr(this object input)
        {
            var t = input.GetType();
            var sb = new StringBuilder();
            foreach (var property in t.GetProperties())
            {
                var attrName = property.GetMethod.Name.Replace("get_", "");
                var attrValue = property.GetValue(input);
                sb.Append($"{attrName}:{attrValue}&");
            }

            return sb.ToString().DelLastChar('&');
        }

        /// <summary>
        /// 反射获取类中非公有成员
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public static T GetNonPublicField<T>(this object input, string fieldName)
        {
            const BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
            var type = input.GetType();
            var field = type.GetField(fieldName, flag);
            var to = default(T);
            try
            {
                if (field != null) to = (T)field.GetValue(input);
            }
            catch { }

            return to;
        }

        /// <summary>
        /// 反射获取类中公有成员
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public static T GetPublicField<T>(this object input, string fieldName)
        {
            const BindingFlags flag = BindingFlags.Instance | BindingFlags.Public;
            var type = input.GetType();
            var field = type.GetField(fieldName, flag);
            var to = default(T);
            try
            {
                if (field != null) to = (T)field.GetValue(input);
            }
            catch { }

            return to;
        }

        /// <summary>
        /// 反射获取类成员的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public static object GetMemberValue(this object input, string fieldName)
        {
            var type = input.GetType();
            try
            {
                object value = type.GetProperty(fieldName).GetValue(input, null);
                return value;
            }
            catch (NullReferenceException)
            {
                return null;
            }
        }

        /// <summary>
        /// 反射获取类成员的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public static object GetNonPublicMemberValue(this object input, string fieldName)
        {
            const BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
            var type = input.GetType();
            try
            {
                object value = type.GetProperty(fieldName, flag).GetValue(input, null);
                return value;
            }
            catch (NullReferenceException)
            {
                return null;
            }
        }

        /// <summary>
        /// 字段赋值
        /// </summary>
        /// <param name="input"></param>
        /// <param name="fieldName"></param>
        /// <param name="value"></param>
        public static void SetField(this object input, string fieldName, object value)
        {
            const BindingFlags flag = BindingFlags.Instance | BindingFlags.Public;
            var type = input.GetType();
            var field = type.GetField(fieldName, flag);
            if(field.NotNull())
                field.SetValue(input, value);
        }

        /// <summary>
        /// 转匿名类
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="sample"></param>
        /// <returns></returns>
        public static T ToDynamic<T>(this object input, T sample)
        {
            if (input == null)
                throw new ArgumentNullException(nameof(input));
            if (sample == null)
                throw new ArgumentNullException(nameof(sample));

            return (T)input;
        }

        /// <summary>
        /// 集合是否为null或空
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsNullOfEmpty<T>(this IEnumerable<T> input)
        {
            return input == null || input.Count() < 1 ;
        }

        /// <summary>
        /// List赋值到指定集合中
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="target"></param>
        public static IList<T> Clone<T>(this IList<T> input)
        {
            return input.Select(item => item.Clone()).ToList(); 
        }

        /// <summary>
        /// 用序列化的方式对引用对象完成深拷贝
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <returns></returns>
        public static T Clone<T>(this T input)
        {
            using (Stream objectStream = new MemoryStream())
            {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(objectStream, input);
                objectStream.Seek(0, SeekOrigin.Begin);

                return (T)formatter.Deserialize(objectStream);
            }
        }

        /// <summary>
        /// 查询子集
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        public static List<T> FindChildren<T>(this List<T> input, T target)
        {
            var list = input.FindAll(a => a.GetMemberValue("parentId").Equals(target.GetMemberValue("id")));
            list.ForEach(item =>
            {
                var children = FindChildren(input, item);
                if (children.NotNull())
                    item.SetField("children", children);
            });

            return list;
        }

        /// <summary>
        /// 对象拷贝
        /// </summary>
        /// <param name="input"></param>
        /// <param name="target"></param>
        public static void CopyTo(this object input, object target)
        {
            var inType = input.GetType();
            var outType = target.GetType();
            foreach (var itemOut in outType.GetProperties())
            {
                var itemIn = inType.GetProperty(itemOut.Name);
                if (itemIn != null)
                {
                    var value = itemIn.GetValue(input);
                    if(value.NotNull())
                        itemOut.SetValue(target, value);
                }
            }
        }

        /// <summary>
        /// 改变对象类型
        /// </summary>
        /// <param name="input"></param>
        /// <param name="targetType"></param>
        /// <returns></returns>
        public static object ChangeType(this object input, Type targetType)
        {
            var type = input.GetType();
            if (targetType == type)
                return input;

            switch (targetType)
            {
                case var t when t.FullName.Contains("System.Int16"):
                    return Convert.ToInt16(input);
                case var t when t.FullName.Contains("System.Int32"):
                    return Convert.ToInt32(input);
                case var t when t.FullName.Contains("System.Int64"):
                    return Convert.ToInt64(input);
                case var t when t.FullName.Contains("System.Decimal"):
                    return Convert.ToDecimal(input);
            }

            return input;
        }

        /// <summary>
        /// 串联数组为字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="c">串联分隔符 默认","</param>
        /// <returns></returns>
        public static string Join<T>(this IEnumerable<T> input, char c = ',')
        {
            if (input.IsNullOfEmpty())
                return string.Empty;

            return string.Join(c, input);
        }

        /// <summary>
        /// foreach扩展 (item,index) => {}
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> input, Action<T, int> action)
        {
            if (input == null || input.Count() == 0)
                return input;
            var index = 0;
            input.ToList().ForEach(item =>  action(item, index++));

            return input;
        }


    }
}
